package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	// http://localhost:8080/user/1234
	r.GET("/user/:id", basicPathParams)

	// http://localhost:8080/user/42/order/789
	r.GET("/user/:id/order/:order_id", multiPathParams)

	// http://localhost:8080/files/docs/readme.txt
	r.GET("/files/*filepath", optionalPathParams)

	// http://localhost:8080/search?keyword=gin
	r.GET("/search", getQueryParams)

	// http://localhost:8080/search
	r.GET("/search/default", getDefaultQueryParams)

	// http://localhost:8080/filter?category=books&price=20
	r.GET("/filter", queryMultiParams)

	// http://localhost:8080/tags?tag=go&tag=gin&tag=web
	r.GET("/tags", queryArray)

	// http://localhost:8080/users?user[name]=alice&user[age]=25
	r.GET("/users", queryMap)

	/*
		curl --location --request GET 'http://localhost:8080/login' \
		--data-urlencode 'username=admin' \
		--data-urlencode 'password=123456'
	*/
	r.POST("/login", postForm)

	r.POST("/register", defaultPostForm)

	/*
		curl --location --request POST 'http://localhost:8080/upload' \
		--form 'file=@"/Users/zyk/goprojects/gin_study/go.sum"'
	*/
	r.POST("/upload", formFile)

	r.POST("/multi-upload", multipartForm)

	r.POST("/json", parseJson)

	r.POST("/xml", parseXml)

	r.POST("/bind", bindByContentType)

	r.POST("/binding/json", bindingJson)

	r.GET("/binding/query", bindingQuery)

	r.POST("/binding/login", bindingForm)
	r.GET("/binding/user/:id", bindingUri)

	r.POST("/binding/xml", bindingXml)

	r.GET("/binding/header", bindingHeader)

	r.POST("/binding/auto/json", binding)
	r.POST("/binding/auto/xml", binding)
	r.POST("/binding/auto/form", binding)

	r.Run(":8080")
}

// basicPathParams 基本路径参数
func basicPathParams(c *gin.Context) {
	id := c.Param("id") // 获取路径参数
	c.JSON(http.StatusOK, gin.H{
		"user_id": id,
	})
}

// basicPathParams 基本路径参数
func multiPathParams(c *gin.Context) {
	id := c.Param("id")
	orderID := c.Param("order_id")

	c.JSON(http.StatusOK, gin.H{
		"user_id":  id,
		"order_id": orderID,
	})
}

// optionalPathParams 可选路径参数
func optionalPathParams(c *gin.Context) {
	filepath := c.Param("filepath") // 获取通配符路径
	c.JSON(http.StatusOK, gin.H{
		"filepath": filepath,
	})
}

// getQueryParams 获取查询参数
func getQueryParams(c *gin.Context) {
	keyword := c.Query("keyword") // 获取查询参数
	c.JSON(http.StatusOK, gin.H{
		"keyword": keyword,
	})
}

// getDefaultQueryParams 设置默认值
func getDefaultQueryParams(c *gin.Context) {
	keyword := c.DefaultQuery("keyword", "default")
	c.JSON(http.StatusOK, gin.H{
		"keyword": keyword,
	})
}

// queryMultiParams 获取多个查询参数
func queryMultiParams(c *gin.Context) {
	category := c.Query("category")
	price := c.Query("price")

	c.JSON(http.StatusOK, gin.H{
		"category": category,
		"price":    price,
	})
}

// queryArray 获取数组查询参数
func queryArray(c *gin.Context) {
	tags := c.QueryArray("tag") // 获取多个 tag 参数
	c.JSON(http.StatusOK, gin.H{
		"tags": tags,
	})
}

// queryMap 获取查询参数映射
func queryMap(c *gin.Context) {
	users := c.QueryMap("user") // 解析 user[name]=xxx&user[age]=xxx
	c.JSON(http.StatusOK, gin.H{
		"users": users,
	})
}

// postForm 获取查询参数映射
func postForm(c *gin.Context) {
	username := c.PostForm("username")
	password := c.PostForm("password")

	c.JSON(http.StatusOK, gin.H{
		"username": username,
		"password": password,
	})
}

// defaultPostForm 获取查询参数映射
func defaultPostForm(c *gin.Context) {
	username := c.DefaultPostForm("username", "guest")
	role := c.DefaultPostForm("role", "user")

	c.JSON(http.StatusOK, gin.H{
		"username": username,
		"role":     role,
	})
}

// formFile 获取查询参数映射
func formFile(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to upload file"})
		return
	}

	// 保存文件
	dst := "./uploads/" + file.Filename
	c.SaveUploadedFile(file, dst)

	c.JSON(http.StatusOK, gin.H{
		"filename": file.Filename,
		"size":     file.Size,
	})
}

// multipartForm 获取查询参数映射
func multipartForm(c *gin.Context) {
	form, err := c.MultipartForm()
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid form data"})
		return
	}

	files := form.File["files"]
	var filenames []string

	for _, file := range files {
		dst := "./uploads/" + file.Filename
		c.SaveUploadedFile(file, dst)
		filenames = append(filenames, file.Filename)
	}

	c.JSON(http.StatusOK, gin.H{"uploaded_files": filenames})
}

type User struct {
	Name  string `json:"name" xml:"name" form:"name" binding:"required"`
	Email string `json:"email" xml:"email" form:"email"  binding:"required,email"`
	Age   int    `json:"age"  xml:"age" form:"age"  binding:"omitempty,gte=18,lte=60"`
}

func parseJson(c *gin.Context) {
	var user User
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "User received", "user": user})
}

type UserXML struct {
	Name  string `xml:"name" binding:"required"`
	Email string `xml:"email" binding:"required,email"`
	Age   int    `xml:"age"`
}

func parseXml(c *gin.Context) {
	var user UserXML
	if err := c.ShouldBindXML(&user); err != nil {
		c.XML(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.XML(http.StatusOK, gin.H{"message": "User received", "user": user})
}

// 根据 Content-Type 类型自动绑定
func bindByContentType(c *gin.Context) {
	var user User
	// 根据 Content-Type 类型自动绑定
	if err := c.ShouldBind(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "User received", "user": user})
}

func bindingJson(c *gin.Context) {
	var user User
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "User received", "user": user})
}

type QueryParams struct {
	Name  string `form:"name" binding:"required"`
	Email string `form:"email" binding:"required,email"`
}

func bindingQuery(c *gin.Context) {
	var params QueryParams
	if err := c.ShouldBindQuery(&params); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Query received", "params": params})
}

type LoginForm struct {
	Username string `form:"username" binding:"required"`
	Password string `form:"password" binding:"required"`
}

func bindingForm(c *gin.Context) {
	var form LoginForm
	if err := c.ShouldBind(&form); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	//if err := c.ShouldBindWith(&form, binding.Form); err != nil {
	//	c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	//	return
	//}

	c.JSON(http.StatusOK, gin.H{"message": "Login successful", "user": form.Username})
}

type URIParams struct {
	ID int `uri:"id" binding:"required"`
}

func bindingUri(c *gin.Context) {
	var params URIParams
	if err := c.ShouldBindUri(&params); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"user_id": params.ID})
}

func bindingXml(c *gin.Context) {
	var user UserXML
	if err := c.ShouldBindXML(&user); err != nil {
		c.XML(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.XML(http.StatusOK, gin.H{"message": "User received", "user": user})
}

type HeaderParams struct {
	RequestID string `header:"X-Request-ID" binding:"required"`
}

func bindingHeader(c *gin.Context) {
	var header HeaderParams
	if err := c.ShouldBindHeader(&header); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"request_id": header.RequestID})
}

func binding(c *gin.Context) {
	var user User
	if err := c.ShouldBind(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "User received", "user": user})
}
